package com.hqyj.onlineExamination.modules.common.controller;

import com.hqyj.onlineExamination.modules.common.entity.ExceptionLog;
import com.hqyj.onlineExamination.modules.common.service.ExceptionLogService;
import com.hqyj.onlineExamination.util.IPUtil;
import com.hqyj.onlineExamination.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;

//控制层统一异常处理控制器，处理不同的异常逻辑（捕获不同的异常，处理不同的业务逻辑）
@ControllerAdvice   //@ControllerAdvice：捕获所有的控制器异常
public class ExceptionHandlerController {

    private final static Logger LOGGER =
            LoggerFactory.getLogger(ExceptionHandlerController.class);
    @Autowired
    private ExceptionLogService exceptionLogService;

    @ExceptionHandler(value = Exception.class)  //捕获类型为Exception的异常
    public ModelAndView exeptionHandler(HttpServletRequest request, Exception exception) {

        // 将异常信息插入数据库
        insertExceptionLog(request, exception);

        //定义初始值
        String message = "";
        String data = "";
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

        //如果是权限异常
        if (exception instanceof UnauthorizedException) {
            message = "No permission";
            data = "/common/403";
            httpStatus = HttpStatus.FORBIDDEN;  //403的状态码
        } else {
            message = "Internal server error";
            data = "/common/500";
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;  //500的状态码
        }

        // 根据请求的方式决定返回的方式
        // 如果是接口，返回json数据
        if (isInterface(request)) {
            return jsonResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                    message, data);//data：装路径地址(要跳转的页面)
        } else {    // 如果是页面的访问，返回500页面
            return pageResult(data, httpStatus);
        }
    }

    /**
     * - 返回错误页面
     */
    //url：错误的页面路径
    //ModelAndView可以同时处理返回页面和返回数据
    public ModelAndView pageResult(String url, HttpStatus httpStatus) {
        //重定向到错误页面
        return new ModelAndView("redirect:" + url, httpStatus);
    }

    /**
     * - 返回 Json 数据
     */
    public ModelAndView jsonResult(int status, String message, Object data) {
        //new MappingJackson2JsonView()之后可以在modelAndView对象中addObject
        ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
        modelAndView.addObject("status", status);//设置状态码
        modelAndView.addObject("message", message);//设置信息
        modelAndView.addObject("data", data);//设置data
        return modelAndView;
    }

    /**
     * - 根据方法和类上面的注解来判断是否为接口
     */
    public Boolean isInterface(HttpServletRequest request) {
        //拿到目标方法的信息
        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");//request对象里的一个键值，通过调试操作可以看到
        //得到方法的注解
        Annotation[] annotationsForMethod =
                handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class);//ResponseBody注解
        //得到类的注解
        Annotation[] annotationsForClazz =
                handlerMethod.getBeanType().getAnnotationsByType(RestController.class);//RestController注解
        //得到类的注解
        Annotation[] annotationsForClazz2 =
                handlerMethod.getBeanType().getAnnotationsByType(ResponseBody.class);//ResponseBody注解
        //有没有注解
        return annotationsForMethod.length > 0 ||
                annotationsForClazz.length > 0 ||
                annotationsForClazz2.length > 0;
    }

    //插入异常的信息
    public void insertExceptionLog(HttpServletRequest request, Exception ex) {
        ex.printStackTrace();
        LOGGER.debug("======== Log exception into db ========");

        ExceptionLog exceptionLog = new ExceptionLog();
        exceptionLog.setIp(IPUtil.getIpAddr(request));//设置ip
        exceptionLog.setPath(request.getServletPath());//设置path路径
        //获取目标方法的消息
        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        if (handlerMethod != null) {
            exceptionLog.setClassName(handlerMethod.getBeanType().getName());//设置目标类的名字
            exceptionLog.setMethodName(handlerMethod.getMethod().getName());//设置目标方法的名字
        }
        exceptionLog.setExceptionType(ex.getClass().getSimpleName());//设置类型
        //设置message信息，以byte的长度截取200个，存入数据库
        exceptionLog.setExceptionMessage(StringUtils.isBlank(ex.getMessage()) ? "" ://判断是否为空
                StringUtil.splitString(ex.getMessage().trim(), 200));

        //插入到数据库
        exceptionLogService.insertExceptionLog(exceptionLog);
    }

    /*//捕获没有权限异常，这里假定算数异常为403权限异常
    @ExceptionHandler(value = ArithmeticException.class)
    public ModelAndView exceptionHandlerFor403(HttpServletRequest request,
                                               Exception exception) {
        insertExceptionLog(request, exception);
        if (isInterface(request)) {
            return jsonResult(ResultEntity.ResultStatus.FAILED.status,
                    "No permission", "/common/403");
        } else {
            return pageResult("/common/403", HttpStatus.FORBIDDEN);
        }
    }*/

}



